package com.happywynne.elasticsearchtravel.util;

import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;

import java.io.IOException;
import java.util.List;
import java.util.Optional;

/**
 * @author Wynne.Lu
 * @date 2021/1/14 下午10:08
 * @desc
 */
public class ElasticSearchUtil {

    private static RestHighLevelClient restHighLevelClient;

    static {
        restHighLevelClient = SpringContextUtil.getBean(RestHighLevelClient.class);
    }

    private ElasticSearchUtil() {
    }

    public static CreateIndexResponse createIndex(String index) throws IOException {
        CreateIndexRequest cir = new CreateIndexRequest(index);
        return restHighLevelClient.indices().create(cir, RequestOptions.DEFAULT);
    }

    public static boolean existsIndex(String index) throws IOException {
        GetIndexRequest gir = new GetIndexRequest(index);
        return restHighLevelClient.indices().exists(gir, RequestOptions.DEFAULT);
    }

    public static AcknowledgedResponse deleteIndex(String index) throws IOException {
        DeleteIndexRequest dir = new DeleteIndexRequest(index);
        return restHighLevelClient.indices().delete(dir, RequestOptions.DEFAULT);
    }

    public static <T> IndexResponse insert(String index, T obj) throws IOException {
        IndexRequest ir = new IndexRequest(index);
        ir.timeout(TimeValue.timeValueMinutes(1));
        ir.source(JSON.toJSONString(obj), XContentType.JSON);
        return restHighLevelClient.index(ir, RequestOptions.DEFAULT);
    }

    public static boolean existsById(String index, String id) throws IOException {
        GetRequest gr = new GetRequest(index, id);
        gr.fetchSourceContext(new FetchSourceContext(false));
        gr.storedFields("_none_");
        return restHighLevelClient.exists(gr, RequestOptions.DEFAULT);
    }

    public static GetResponse getById(String index, String id) throws IOException {
        GetRequest gr = new GetRequest(index, id);
        return restHighLevelClient.get(gr, RequestOptions.DEFAULT);
    }

    public static UpdateResponse updateById(String index, String id, Object obj) throws IOException {
        UpdateRequest ur = new UpdateRequest(index, id);
        ur.timeout(TimeValue.timeValueMinutes(1));
        ur.doc(JSON.toJSONString(obj), XContentType.JSON);
        return restHighLevelClient.update(ur, RequestOptions.DEFAULT);
    }

    public static DeleteResponse deleteById(String index, String id) throws IOException {
        DeleteRequest ur = new DeleteRequest(index, id);
        ur.timeout(TimeValue.timeValueMinutes(1));
        return restHighLevelClient.delete(ur, RequestOptions.DEFAULT);
    }

    public static BulkResponse bulk(List<IndexRequest> requestList) throws IOException {
        BulkRequest br = new BulkRequest();
        br.timeout(TimeValue.timeValueMinutes(1));

        requestList.forEach(br::add);

        return restHighLevelClient.bulk(br, RequestOptions.DEFAULT);
    }

    public static IndexRequest createIndexRequest(String index, Object obj) {
        IndexRequest ir = new IndexRequest(index);
        ir.timeout(TimeValue.timeValueMinutes(1));
        ir.source(JSON.toJSONString(obj), XContentType.JSON);
        return ir;
    }

    public static SearchResponse search(String index, SearchSourceBuilder searchSourceBuilder) throws IOException {
        SearchRequest searchRequest = new SearchRequest(index);

        searchRequest.source(searchSourceBuilder);
        return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    }

    public static CountResponse searchCount(String index, AbstractQueryBuilder query) throws IOException {
        CountRequest countRequest = new CountRequest(index);

        countRequest.query(query);
        return restHighLevelClient.count(countRequest, RequestOptions.DEFAULT);
    }

    public static SearchResponse searchPager(String index, SearchSourceBuilder searchSourceBuilder, Integer from, Integer size) throws IOException {
        SearchRequest searchRequest = new SearchRequest(index);

        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);
        return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    }

    public static Optional<SearchHit> getOrEmpty(SearchResponse response) {
        if (response.getHits().getTotalHits().value == 0) {
            return Optional.empty();
        }
        return Optional.of(response.getHits().getAt(0));
    }

    public static Optional<SearchHits> listOrEmpty(SearchResponse response) {
        if (response.getHits().getTotalHits().value == 0) {
            return Optional.empty();
        }
        return Optional.of(response.getHits());
    }

}
